from pydantic import BaseModel, Field, HttpUrl, EmailStr, UUID1
from typing import Literal, List, Annotated, Optional, Union
from fastapi import APIRouter, Query, Path, Body, Cookie, Header, Response
from fastapi.staticfiles import StaticFiles
import uvicorn

day02 = APIRouter()

class FilterParams(BaseModel):
    # 限制额外的参数
    model_config = {"extra": "forbid"}
    # Field 用于对模型中的字段进行校验
    limit: int = Field(100, gt=0, le=100)
    offset: int = Field(0, ge=0)
    # Literal 表示字段的值必须是其中的某一个
    order_by: Literal["created_at", "updated_at"] = "created_at"
    # tags: list[str] = []
    tags: set[str] = set()

@day02.post("/day02/pydantic_query")
def pydantic_query(query_parameter: FilterParams):
# def pydantic_query(query_parameter: FilterParams = Query()):
# def pydantic_query(query_parameter: Annotated[FilterParams, Query()]):
# def pydantic_query(query_parameter: FilterParams):
    return query_parameter



class CityInfo(BaseModel):
    name: str = Field(..., example="Beijing")  # example是注解的作用，值不会被验证
    country: str
    country_code: str = None  # 给一个默认值
    country_population: int = Field(default=800, title="人口数量", description="国家的人口数量", ge=800)

    class Config:
        json_schema_extra = {
            "example": {
                "name": "Shanghai",
                "country": "China",
                "country_code": "CN",
                "country_population": 1400000000,
            }
        }


@day02.put("/request_body/city/{name}")
def mix_city_info(
    name: str,
    city01: CityInfo,
    city02: CityInfo,  # Body可以是多个的
    confirmed: int = Query(ge=0, description="确诊数", default=0),
    death: int = Query(ge=0, description="死亡数", default=0),
):
    if name == "Shanghai":
        return {"Shanghai": {"confirmed": confirmed, "death": death}}
    return city01.dict(), city02.dict()



class Image(BaseModel):
    url: HttpUrl
    name: str

'''
    模型的嵌套
'''
class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    email: EmailStr
    tax: Optional[float] = None
    tags: set[str] = set()
    # 子模型
    image: Optional[Image] = None


@day02.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results

# 参数指定为一个列表
@day02.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
    return images


@day02.get("/set-cookie/")
async def set_cookie(response: Response):
    response.set_cookie(key="jesse_create_cookie", value="helloworld")
    return {"message": "Cookie 设置成功"}

# Cookie参数
@day02.get("/cookie/")
def read_cookie(jesse_create_cookie: Annotated[Optional[str], Cookie()]):
    return jesse_create_cookie

@day02.get("/header")
def header(user_agent: Optional[str] = Header(None, convert_underscores=True), x_token: List[str] = Header(None)):
    """
    有些HTTP代理和服务器是不允许在请求头中带有下划线的，所以Header提供convert_underscores属性让设置
    :param user_agent: convert_underscores=True 会把 user_agent 变成 user-agent
    :param x_token: x_token是包含多个值的列表
    :return:
    """
    print("User-Agent",user_agent)
    print("x_token",x_token)
    return {"User-Agent": user_agent, "x_token": x_token}



# 响应模型
@day02.post("/response/item", response_model=Item)
def get_items():
    item_dict = {
          "name": "aaa",
          "description": "aaa",
          "price": 0,
          "email": "user@example.com",
          "tax": 0,
          "tags": [],
          "image": {
            "url": "https://example.com/",
            "name": "string"
          }
    }
    item = Item(**item_dict)
    return item

# 响应模型列表
@day02.post("/response/items", response_model=List[Item])
def get_all_items():
    item_dict = {
          "name": "aaa",
          "description": "aaa",
          "price": 0,
          "email": "user@example.com",
          "tax": 0,
          "tags": [],
          "image": {
            "url": "https://example.com/",
            "name": "string"
          }
    }
    item1 = Item(**item_dict)
    item2 = Item(**item_dict)
    return [item2, item1]


# 用户模型
class UserInfo(BaseModel):
    name: str
    email: EmailStr
    age: int = 0
    password: str = Field(min_length=8, max_length=16)

# 定义输出模型
class UserOut(BaseModel):
    name: str
    email: EmailStr
    age: int = 0

'''
    response_model_include 指定哪些字段包含在输出中
    response_model_exclude 指定哪些字段排除在输出中
'''
@day02.post("/users/add", response_model=UserOut, response_model_include=['name', 'age'], response_model_exclude=['age'])
def create_user(user: UserInfo):
    return user


class Itema(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: List[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


""" 
	response_model_exclude_unset=True
	表示默认值不包含在响应中，仅包含实际给的值
	如果实际给的值与默认值相同也会包含在响应中
"""
@day02.get("/itema/{item_id}", response_model=Itema, response_model_exclude_unset=False)
async def read_item(item_id: str):
    return items[item_id]



class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Optional[str] = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Optional[str] = None


class UserInDB(BaseModel):
    username: str
    hashed_password: str
    email: EmailStr
    full_name: Optional[str] = None


def fake_password_hasher(raw_password: str):
    return "supersecret" + raw_password


def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
    print("执行把user_in_db对象写入到数据表的操作")
    return user_in_db


@day02.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
    user_saved = fake_save_user(user_in)
    return user_saved


'''
    作业：
    1. 昨天学生相关的操作继续完成，使用pydantic模型，增加输出模型
    2. 设计学生信息表，参照<<沃林学生信息表>>
    3. 增加沃林学生考核成绩表，参照<<沃林学生考核成绩表>>
'''

'''
     2. 设计学生信息表，参照<<沃林学生信息表>>
     设计思路：
        1. 设计一个表，确定表结构：
            表名：stu_db
            字段和数据类型：
                学生编号： 字符串     主键
                班级编号： 字符串     外键    引用子班级表的数据    class_db
                姓名：
                籍贯：
                毕业院校：
                专业：
                毕业时间：
                入学日期：
                学历：
                顾问编号：
                性别：
                年龄：
            可以用列表进行学生信息的存储
        2. 确定校验规则
            学生编号的定义规则：
                不让前端传递，自己根据班级生成
                让前端传，只做校验（进行自定义编码后再校验）
            班级编号：
                枚举
                下拉框
                校验班级编号是否存在
            其它字段按照正常的认知，根据需求来进行校验
        3. 编写接口：
            1. 查询所有学生的信息（分页查询：页码，每页多少条）
            2. 查询单个学生的信息（根据编号，根据姓名）
            3. 根据班级查询学生信息
            4. 添加学生信息  post， 请求模型   校验  返回模型
            5. 修改学生信息：根据学生编号  请求模型：学生模型
                判断学生是否存在
                校验
                返回模型
            6. 删除学生信息：学生编号
                学生是否存在
        4. 测试
'''

